#include <gl\glui.h>
#include <math.h>
#include <iostream>
#include "RGBpixmap.h"
#include <string>

// quadricas
GLUquadric* glQ;	// nec. p/ criar sup. quadraticas (cilindros, esferas...)

// dimensoes e localizacao da janela
#define DIMX 500
#define DIMY 500
#define INITIALPOS_X 200
#define INITIALPOS_Y 200

int TRUE  = 1;
int FALSE = 0;

double PI = 3.1415926;

float xy_aspect;

// matriz de transf. geometrica utilizada pelo botao esferico
float view_rotate[16] = { 1,0,0,0,
	0,1,0,0,
	0,0,1,0,
	0,0,0,1 };

// vector de posicao utilizado pelo botao de afastamento
float obj_pos[] = { 0.0, 0.0, 0.0 };

// declara??es para os tres eixos (cilindros)
double axis_radius_begin =  0.4;
double axis_radius_end   =  0.0;
double axis_lenght       = 32.0;
int axis_nslices = 8;
int axis_nstacks = 1;

// declaracoes para a skybox
float skybox_shininess[] = {128.0}; 
float skybox_specular[] = {0.0, 0.0, 0.0, 0.0};	/* specular reflection. */
float skybox_diffuse[] =  {0.0, 0.0, 0.0, 0.0};	/* diffuse reflection. */
float skybox_ambient[] =  {1.0, 1.0, 1.0, 1.0};	/* ambient reflection. */
float skybox_bottom_r = 0.5, skybox_bottom_g = 0.5, skybox_bottom_b = 0.5;
float light_ambient_skybox[] = {1.0, 1.0, 1.0, 1.0}; // Ambient light for when rendering the skybox

// declaracoes para as paredes
float wall_shininess[] = {128.0}; 
float wall_specular[] = {0.5, 0.5, 0.5, 1.0};	/* specular reflection. */
float wall_diffuse[] =  {0.7, 0.7, 0.7, 1.0};	/* diffuse reflection. */
float wall_ambient[] =  {0.7, 0.7, 0.7, 1.0};	/* ambient reflection. */

// declaracoes para as madeiras
float wood_shininess[] = {128.0}; 
float wood_specular[] = {0.6, 0.6, 0.6, 1.0};	/* specular reflection. */
float wood_diffuse[] =  {0.7, 0.7, 0.7, 1.0};	/* diffuse reflection. */
float wood_ambient[] =  {0.6, 0.6, 0.6, 1.0};	/* ambient reflection. */

// declaracoes para os metais
float metal_shininess[] = {128.0}; 
float metal_specular[] = {0.574, 0.574, 0.574, 1.0};	/* specular reflection. */
float metal_diffuse[] =  {0.5, 0.5, 0.5, 1.0};	/* diffuse reflection. */
float metal_ambient[] =  {0.45, 0.45, 0.45, 1.0};	/* ambient reflection. */

//declaracoes para os papeis
float paper_shininess[] = {128.0}; 
float paper_specular[] = {0.1, 0.1, 0.1, 1.0};	/* specular reflection. */
float paper_diffuse[] =  {0.7, 0.7, 0.7, 1.0};	/* diffuse reflection. */
float paper_ambient[] =  {0.6, 0.6, 0.6, 1.0};	/* ambient reflection. */

// declaracoes de texturas

char skybox_texture[] = "tex/paisagem.bmp";
int skybox_texture_number = 1;

char wall_texture[] = "tex/wall.bmp";
int wall_texture_number = 2;

char printer_texture[] = "tex/wood1.bmp";
int printer_texture_number = 3;

char carpet_side_texture[] = "tex/wood2.bmp";
int carpet_side_texture_number= 4;

char carpet_texture[] = "tex/rubber.bmp";
int carpet_texture_number = 5;

char roll_texture[] = "tex/roll.bmp";
int roll_texture_number = 6;

char cutter_texture[] = "tex/cutter.bmp";
int cutter_texture_number = 7;

char floor_texture[] = "tex/floor.bmp";
int floor_texture_number = 8;

char metal_texture[] = "tex/metal.bmp";
int metal_texture_number = 9;

int STATIC_SCENE_LIST = 1;
int PRINTER_LIST = 2;
int ROBOT_LIST = 3;

// declara??es para a fonte de luz LIGHT0;
float light0_position[]  = {-12.0, 13.0, 1.0, 1.0}; // nao necessaria...
float light0_ambient[] =   {0.0, 0.0, 0.0, 1.0}; // sem componente ambiente
float light0_diffuse[] =   {10, 10, 10, 1.0};
float light0_specular[] =  {10, 10, 10, 1.0};
float light0_kc = 0.0;
float light0_kl = 1.0;
float light0_kq = 0.0;
double light0x = -12;
double light0y = 13;
double light0z = 1.0;
double symb_light0_radius = 0.2;
int symb_light0_slices = 8;
int symb_light0_stacks =8;

// declara??es para a fonte de luz LIGHT1;
float light1_position[]  = {4, 4, 1.7, 1 }; // nao necessaria...
float light1_ambient[] =   {0.0, 0.0, 0.0, 1.0}; // sem componente ambiente
float light1_diffuse[] =   {150, 150, 150, 1.0};
float light1_specular[] =  {150, 150, 150, 1.0};
float light1_kc = 1.0;
float light1_kl = 0.0;
float light1_kq = 0.0;
double light1x = 1;
double light1y = 1;
double light1z = 1;
double ang = PI/3.5;
float spot_direction[] = {cos(ang), -sin(ang),0};

// fonte (global) de luz ambiente 
float light_ambient[] = {0.7, 0.7, 0.7, 1.0}; /* Set the background ambient lighting. */

//////////////////////////////////////////////////////////////////////////////////////////
////All objects are drawn through relative proportions instead of fixed metric values ////
////         enabling easy scaling of type of object within the scene                 ////
//////////////////////////////////////////////////////////////////////////////////////////


//MACRO VARS
int DELTA_T = 5; //delta time interpolation
int scale = 10;  //scene scale
int maxRobotLoad = 4; //Robot max load before starting trip
double sSpeed = 1; //animation speed


//ANIMATION
//-STATES
int CLEAVER_ENABLED=1,
	CARPET_ENABLED=1,
	PRESS_ENABLED=1,
	ROBOT_ENABLED=0,
	PAPER_ENABLED=1,
	FALLINGPAPER_ENABLE=0,
	DRAWROBOTPAPER_ENABLE=0;

//-Carpet
double move=0.0;
double carpetSpeed=0.0084*sSpeed;

//-Cylinder
double cylRot=0.0;

//-Cleaver
double clvPos=0.0;
double clvDelta=0.02*sSpeed;

//-Robot
double robotPos=0.0;
double robotX=0,robotZ=0;
double speed=0.002*sSpeed;
double robotAng=0;
int robotLoad = 0;
bool robot_light = true;

//-Paper
double largePaperSize = 0;
double maxPaperSize = scale*2.15;
double paperTexturePos = 0;
double paperSpeed = 0.02*sSpeed;
double paperFallSpeed = 0.016*sSpeed;
double paperFallHeight = 0;
double paperFallBottom = scale*0.08;
double paperFallLead = 0.055*sSpeed;
double paperFallX = 0;

//SWITCHS
bool help_on = false;
int textures_e=1,wireframe_e=0,showReferencial=0;

//CAMERAS
int camera = 1;
double cam3x=0,cam3y=55,cam3z=0,cam3s=0.5;

// variaveis para a janela
int main_window;
GLUI *glui2;

RGBpixmap pixmap;

double textRComponent = 1;
double textGComponent = 1;
double textBComponent = 0;

int cameraTextX = 5;
int cameraTextY = 480;

double evaluatorXSegments = 60;
double evaluatorYSegments = 60;

void useMetalMaterial() 
{
	glMaterialfv(GL_FRONT_AND_BACK, GL_SHININESS, metal_shininess);
	glMaterialfv(GL_FRONT_AND_BACK, GL_SPECULAR,  metal_specular);
	glMaterialfv(GL_FRONT_AND_BACK, GL_DIFFUSE,   metal_diffuse);
	glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT,   metal_ambient);
	glDisable(GL_COLOR_MATERIAL);
}

void useWallMaterial() 
{
	glMaterialfv(GL_FRONT_AND_BACK, GL_SHININESS, wall_shininess);
	glMaterialfv(GL_FRONT_AND_BACK, GL_SPECULAR,  wall_specular);
	glMaterialfv(GL_FRONT_AND_BACK, GL_DIFFUSE,   wall_diffuse);
	glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT,   wall_ambient);
	glDisable(GL_COLOR_MATERIAL);
}

void usePaperMaterial() 
{
	glDisable(GL_COLOR_MATERIAL);
	glMaterialfv(GL_FRONT_AND_BACK, GL_SHININESS, paper_shininess);
	glMaterialfv(GL_FRONT_AND_BACK, GL_SPECULAR,  paper_specular);
	glMaterialfv(GL_FRONT_AND_BACK, GL_DIFFUSE,   paper_diffuse);
	glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT,	  paper_ambient);
}

void useWoodMaterial() 
{
	glMaterialfv(GL_FRONT_AND_BACK, GL_SHININESS, wood_shininess);
	glMaterialfv(GL_FRONT_AND_BACK, GL_SPECULAR,  wood_specular);
	glMaterialfv(GL_FRONT_AND_BACK, GL_DIFFUSE,   wood_diffuse);
	glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT,   wood_ambient);
	glDisable(GL_COLOR_MATERIAL);
}

void drawText(double r, double g, double b, int x, int y, std::string text) {
	glDisable(GL_DEPTH_TEST);
	glMatrixMode(GL_PROJECTION);
	glLoadIdentity();
	glOrtho(0,500,0,500,-1.0,1.0);
	glMatrixMode(GL_MODELVIEW);
	glLoadIdentity();

	glDisable(GL_LIGHTING);
	glEnable(GL_COLOR_MATERIAL);
	glPushMatrix();
	glColor3f(r, g, b);		// amarelo
	glRasterPos3f(x, y, 0);

	for (unsigned i = 0; i < text.size(); ++i) {
		glutBitmapCharacter(GLUT_BITMAP_HELVETICA_18, text[i]);
	}

	glPopMatrix();

	glDisable(GL_COLOR_MATERIAL);
	glEnable(GL_LIGHTING);
	glEnable(GL_DEPTH_TEST);
}

void drawHelpText() {
	std::string text;
	int tempy = 480;
	int ystep = 22;

	tempy -= ystep;
	drawText(textRComponent, textGComponent, textBComponent, 5, tempy, "Esc: Quit\n");
	tempy -= ystep;
	drawText(textRComponent, textGComponent, textBComponent, 5, tempy, "1, 2, 3: Change camera\n");
	tempy -= ystep;
	drawText(textRComponent, textGComponent, textBComponent, 5, tempy, "t, y: Inc./Dec. light intensity\n");
	tempy -= ystep;
	drawText(textRComponent, textGComponent, textBComponent, 5, tempy, "e, s, d, f, w, r: Change light position");
	tempy -= ystep;
	drawText(textRComponent, textGComponent, textBComponent, 5, tempy, "j, l, k, i, q, a, h: Change camera's position (cam. 3 only)");
}

void drawSkybox() {
	float old_ambient[4];
	glGetFloatv(GL_LIGHT_MODEL_AMBIENT, old_ambient);

	double westX = -12.0, westMinY = -12.0, westMaxY = 24.0, westMinZ = -16.0, westMaxZ = 50.0;
	float westNormalX = 1.0, westNormalY = 0.0, westNormalZ = 0.0;
	float westTextureReps = 1.0;

	double northMinX = -12.0, northMaxX = 50.0, northMinY = -12.0, northMaxY = 24.0, northZ = -16.0;
	float northNormalX = 1.0, northNormalY = 0.0, northNormalZ = 0.0;
	float northTextureReps = 1.0;

	float skyboxBottomNormalX = 0.0, skyboxBottomNormalY = 1.0, skyboxBottomNormalZ = 0.0;

	glLightModelfv(GL_LIGHT_MODEL_AMBIENT, light_ambient_skybox);

	glDisable(GL_COLOR_MATERIAL);
	// Definicao de material a usar daqui em diante (valores declarados acima)
	glMaterialfv(GL_FRONT_AND_BACK, GL_SHININESS, skybox_shininess);
	glMaterialfv(GL_FRONT_AND_BACK, GL_SPECULAR,  skybox_specular);
	glMaterialfv(GL_FRONT_AND_BACK, GL_DIFFUSE,   skybox_diffuse);
	glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT,   skybox_ambient);

	glEnable(GL_TEXTURE_2D);

	glBindTexture(GL_TEXTURE_2D, skybox_texture_number);
	glBegin(GL_POLYGON);
	glNormal3d(westNormalX, westNormalY, westNormalZ);
	glTexCoord2f(0.0, westTextureReps); 
	glVertex3d(westX, westMaxY, westMaxZ);
	glTexCoord2f(0.0, 0.0); 
	glVertex3d(westX, westMinY, westMaxZ);
	glTexCoord2f(westTextureReps, 0.0); 
	glVertex3d(westX, westMinY, westMinZ);
	glTexCoord2f(westTextureReps, westTextureReps); 
	glVertex3d(westX, westMaxY, westMinZ);
	glEnd();

	glBegin(GL_POLYGON);
	glNormal3d(northNormalX, northNormalY, northNormalZ);
	glTexCoord2f(0.0, northTextureReps); 
	glVertex3d(northMaxX, northMaxY, northZ);
	glTexCoord2f(northTextureReps, northTextureReps);	
	glVertex3d(northMinX, northMaxY, northZ);
	glTexCoord2f(northTextureReps, 0.0);	
	glVertex3d(northMinX, northMinY, northZ);
	glTexCoord2f(0.0, 0.0); 
	glVertex3d(northMaxX, northMinY, northZ);
	glEnd();

	glDisable(GL_TEXTURE_2D);

	glEnable(GL_COLOR_MATERIAL);
	glColor3f(skybox_bottom_r, skybox_bottom_g, skybox_bottom_b);

	glBegin(GL_POLYGON);
	glNormal3d(skyboxBottomNormalX, skyboxBottomNormalY, skyboxBottomNormalZ);
	glVertex3d(northMaxX, northMinY, westMaxZ);
	glVertex3d(northMaxX, northMinY, westMinZ);
	glVertex3d(northMinY, northMinY, westMinZ);
	glVertex3d(northMinY, northMinY, westMaxZ);
	glEnd();

	glColor3f(0.7,0.7,0.7);
	glDisable(GL_COLOR_MATERIAL);

	glLightModelfv(GL_LIGHT_MODEL_AMBIENT, old_ambient);
}

void drawHalfTorus(int scale, double innerRadius, double outerRadius, int nSides, int nRings) {
	GLdouble plane[] = {0.0, 1.0, 0.0, 0.0};
	glClipPlane(GL_CLIP_PLANE0, plane);

	glEnable(GL_CLIP_PLANE0);
	glutSolidTorus(innerRadius, outerRadius, nSides, nRings);
	glDisable(GL_CLIP_PLANE0);
	glPushMatrix();

	glPopMatrix();
}

void drawCylinder(double height, int slices, int loops, double radius) {
	glPushMatrix();
	gluCylinder(glQ, radius, radius, height, slices, loops);

	glPushMatrix();
	glRotated(180,0,1,0);
	gluDisk(glQ, 0, radius, slices, loops);
	glPopMatrix();

	glPushMatrix();
	glTranslated(0,0,height);
	gluDisk(glQ, 0, radius, slices, loops);
	glPopMatrix();
	glPopMatrix();
}

void drawRobot(int scale) {
	useMetalMaterial();

	double robotPosX = - scale*1.5*0.63, robotPosY = 0.3, robotPosZ = scale*4.7*0.245;
	robotPosX=robotPosZ=0;
	double height = 1.0;
	double length, width; 
	length = width = scale*0.327;

	double wheelThickness = 0.1;
	double wheelOffset = 0.01; //to prevent z-fighting

	double xreps = 3.0, yreps = 1.0;

	double detailColorR = 1, detailColorG = 1, detailColorB = 1;

	double mastHeight = 2.5;
	double mastRadius = 0.1;
	double mastCurveRadius = 0.3;
	double slices = 20, loops = 20;

	glEnable(GL_TEXTURE_2D);
	glBindTexture(GL_TEXTURE_2D, metal_texture_number); //usa a mesma textura do tapete rolante

	//north outside
	glBegin(GL_POLYGON);
	glNormal3d(0,0,-1);
	glTexCoord2f(0.0, 0.0); glVertex3d(robotPosX, robotPosY, robotPosZ);
	glTexCoord2f(0.0, yreps); glVertex3d(robotPosX, robotPosY + height, robotPosZ);
	glTexCoord2f(xreps, yreps); glVertex3d(robotPosX + length, robotPosY + height, robotPosZ);
	glTexCoord2f(xreps, 0.0); glVertex3d(robotPosX + length, robotPosY, robotPosZ);
	glEnd();

	//north inside
	glBegin(GL_POLYGON);
	glNormal3d(0,0,1);
	glTexCoord2f(xreps, 0.0); glVertex3d(robotPosX + length, robotPosY, robotPosZ);
	glTexCoord2f(xreps, yreps); glVertex3d(robotPosX + length, robotPosY + height, robotPosZ);
	glTexCoord2f(0.0, yreps); glVertex3d(robotPosX, robotPosY + height, robotPosZ);
	glTexCoord2f(0.0, 0.0); glVertex3d(robotPosX, robotPosY, robotPosZ);
	glEnd();

	//bottom
	glBegin(GL_POLYGON);
	glNormal3d(0,1,0);
	glTexCoord2f(0.0, 0.0); glVertex3d(robotPosX, robotPosY, robotPosZ);
	glTexCoord2f(xreps, 0.0); glVertex3d(robotPosX, robotPosY, robotPosZ + width);
	glTexCoord2f(xreps, xreps); glVertex3d(robotPosX + length, robotPosY, robotPosZ + width);
	glTexCoord2f(0.0, xreps); glVertex3d(robotPosX + length, robotPosY, robotPosZ);
	glEnd();

	//south inside
	glBegin(GL_POLYGON);
	glNormal3d(0,0,-1);
	glTexCoord2f(0.0, 0.0); glVertex3d(robotPosX, robotPosY, robotPosZ + width);
	glTexCoord2f(0.0, yreps); glVertex3d(robotPosX, robotPosY + height, robotPosZ + width);
	glTexCoord2f(xreps, yreps); glVertex3d(robotPosX + length, robotPosY + height, robotPosZ + width);
	glTexCoord2f(xreps, 0.0); glVertex3d(robotPosX + length, robotPosY, robotPosZ + width);
	glEnd();

	//south outside
	glBegin(GL_POLYGON);
	glNormal3d(0,0,1);
	glTexCoord2f(xreps, 0.0); glVertex3d(robotPosX + length, robotPosY, robotPosZ + width);
	glTexCoord2f(xreps, yreps); glVertex3d(robotPosX + length, robotPosY + height, robotPosZ + width);
	glTexCoord2f(0.0, yreps); glVertex3d(robotPosX, robotPosY + height, robotPosZ + width);
	glTexCoord2f(0.0, 0.0); glVertex3d(robotPosX, robotPosY, robotPosZ + width);
	glEnd();

	//west outside
	glBegin(GL_POLYGON);
	glNormal3d(-1,0,0);
	glTexCoord2f(0.0, 0.0); glVertex3d(robotPosX, robotPosY, robotPosZ);
	glTexCoord2f(xreps, 0.0); glVertex3d(robotPosX, robotPosY, robotPosZ + width);
	glTexCoord2f(xreps, yreps); glVertex3d(robotPosX, robotPosY + height, robotPosZ + width);
	glTexCoord2f(0.0, yreps); glVertex3d(robotPosX, robotPosY + height, robotPosZ);
	glEnd();

	//west inside
	glBegin(GL_POLYGON);
	glNormal3d(1,0,0);
	glTexCoord2f(0.0, yreps); glVertex3d(robotPosX, robotPosY + height, robotPosZ);
	glTexCoord2f(xreps, yreps); glVertex3d(robotPosX, robotPosY + height, robotPosZ + width);
	glTexCoord2f(xreps, 0.0); glVertex3d(robotPosX, robotPosY, robotPosZ + width);
	glTexCoord2f(0.0, 0.0); glVertex3d(robotPosX, robotPosY, robotPosZ);
	glEnd();

	//east outside
	glBegin(GL_POLYGON);
	glNormal3d(-1,0,0);
	glTexCoord2f(0.0, 0.0); glVertex3d(robotPosX + length, robotPosY, robotPosZ);
	glTexCoord2f(xreps, 0.0); glVertex3d(robotPosX + length, robotPosY, robotPosZ + width);
	glTexCoord2f(xreps, yreps); glVertex3d(robotPosX + length, robotPosY + height, robotPosZ + width);
	glTexCoord2f(0.0, yreps); glVertex3d(robotPosX + length, robotPosY + height, robotPosZ);
	glEnd();

	//east inside
	glBegin(GL_POLYGON);
	glNormal3d(1,0,0);
	glTexCoord2f(0.0, yreps); glVertex3d(robotPosX + length, robotPosY + height, robotPosZ);
	glTexCoord2f(xreps, yreps); glVertex3d(robotPosX + length, robotPosY + height, robotPosZ + width);
	glTexCoord2f(xreps, 0.0); glVertex3d(robotPosX + length, robotPosY, robotPosZ + width);
	glTexCoord2f(0.0, 0.0); glVertex3d(robotPosX + length, robotPosY, robotPosZ);
	glEnd();

	glDisable(GL_TEXTURE_2D);

	glEnable(GL_COLOR_MATERIAL);
	glColor3d(detailColorR,detailColorG,detailColorB);

	glPushMatrix();
	glTranslated(robotPosX + robotPosY, robotPosY, robotPosZ - wheelThickness - wheelOffset);
	drawCylinder(wheelThickness, slices, loops, robotPosY);
	glPopMatrix();

	glPushMatrix();
	glTranslated(robotPosX + length - robotPosY, robotPosY, robotPosZ - wheelThickness - wheelOffset);
	drawCylinder(wheelThickness, slices, loops, robotPosY);
	glPopMatrix();

	glPushMatrix();
	glTranslated(robotPosX + length - robotPosY, robotPosY, robotPosZ + width + wheelOffset);
	drawCylinder(wheelThickness, slices, loops, robotPosY);
	glPopMatrix();

	glPushMatrix();
	glTranslated(robotPosX + robotPosY, robotPosY, robotPosZ + width + wheelOffset);
	drawCylinder(wheelThickness, slices, loops, robotPosY);
	glPopMatrix();

	glPushMatrix();
	glTranslated(robotPosX + length - mastRadius, robotPosY + height, robotPosZ + width/2);
	glRotated(-90,1,0,0);
	drawCylinder(mastHeight, slices, loops, mastRadius);	
	glPopMatrix();

	glPushMatrix();
	glTranslated(robotPosX + length + mastRadius*2, robotPosY + height + mastHeight, robotPosZ + width/2);
	drawHalfTorus(scale, mastRadius, mastCurveRadius, slices,loops);
	glPopMatrix();

	glDisable(GL_COLOR_MATERIAL);
}


double wallCurveFunction(double x) {
	return (- cos(x) + 1) * 2;
}

void drawCurvedWall(double scale, int nSteps, double amplitude, int vsteps=10) {
	
	double wallHeight = 1.5*scale, wallLength = 4.7*scale;
	double halfWallLength = wallLength / 2;
	
	double stepInterval = halfWallLength/nSteps; 
	double trigInterval = PI/nSteps;

	double currentTrig = 0; 
	double currentStep = stepInterval; 
	
	double oldStep = 0;
	
	double oldCurve = 0, currentCurve;

	double scaledOldCurve = 0, scaledCurrentCurve;
	
	double per= 5/((double)nSteps*2);
	double p=0;

	glEnable(GL_NORMALIZE);
	useWallMaterial();
	
	double vdelta=vsteps/100.0, vTex=0;
	
	for (int i = 0; i < nSteps; ++i) {
		currentCurve = wallCurveFunction(currentTrig);
		currentTrig += trigInterval;
		
		// Produto escalar de 2 vectores a 3 dimensoes
		// x = a[1]*b[2]-a[2]*b[1]
		// y = a[2]*b[0]-a[0]*b[2]
		// z = a[0]*b[1]-a[1]*b[0]
		
		scaledCurrentCurve = currentCurve * amplitude;

		/*(scaledCurrentCurve - scaledOldCurve, 0, stepInterval)
		(0, wallheight, 0)

		x = 0 - stepInterval
		y = 0
		z = scaledCurrentCurve - scaledOldCurve*/
		for(int j=0; j < vsteps; j++){

			glBegin(GL_POLYGON);
				glNormal3d(-stepInterval, 0, scaledCurrentCurve - scaledOldCurve);
				glTexCoord2d(p, 1-(vTex));            glVertex3d(scaledOldCurve, wallHeight*vTex, oldStep);
				glTexCoord2d(p+per, 1-(vTex));        glVertex3d(scaledCurrentCurve,  wallHeight*vTex, currentStep);
				glTexCoord2d(p+per, 1-(vTex+vdelta)); glVertex3d(scaledCurrentCurve, wallHeight*(vTex+vdelta), currentStep);
				glTexCoord2d(p,   1-(vTex+vdelta));   glVertex3d(scaledOldCurve, wallHeight*(vTex+vdelta), oldStep);
			glEnd();

			glBegin(GL_POLYGON);
				glNormal3d(-stepInterval, 0, scaledOldCurve - scaledCurrentCurve);
				glTexCoord2d(1-(p+per)+per, 1-(vTex+vdelta)); glVertex3d(scaledOldCurve, wallHeight*(vTex+vdelta), wallLength - oldStep);
				glTexCoord2d(1-(p+per), 1-(vTex+vdelta));     glVertex3d(scaledCurrentCurve, wallHeight*(vTex+vdelta), wallLength - currentStep);
				glTexCoord2d(1-(p+per), 1-vTex);              glVertex3d(scaledCurrentCurve, wallHeight*vTex, wallLength - currentStep);
				glTexCoord2d(1-(p+per)+per, 1-vTex);          glVertex3d(scaledOldCurve, wallHeight*vTex, wallLength - oldStep);
			glEnd();
			
			vTex+=vdelta;
		}
		vTex=0;
		p+=per;
		
		oldStep = currentStep;
		oldCurve = currentCurve;
		currentStep += stepInterval;
		scaledOldCurve = scaledCurrentCurve;
	}
}

void drawWestWall(int n){

	useWallMaterial();

	double width = n*4.7;
	double height= n*1.5;
	double w=5,h=1;
	double x=width,y=height;
	
	//again, drawn with relative% within the object instead of static metric values
	double ys[] = {0.18,0.43,0.39};
	double xs[] = {0.22,0.64,0.14};

	double yt[] = {0,0.28,0.61,1};
	double xt[] = {0,0.22,0.86,1};
			
	//which in return also allows us to write the whole holed-wall in a single for-for loop
	for(int i=0; i<3; ++i){
		for(int j=0; j<3; ++j){

			if(!(j == 1 && i == 1)){
				glBegin(GL_POLYGON);
					glNormal3d(1.0, 0.0, 0.0);	
					glTexCoord2f(xt[i]*w  , yt[j]*h);    glVertex3d(0,y             , x);	
					glTexCoord2f(xt[i]*w  , yt[j+1]*h);  glVertex3d(0,y-ys[j]*height, x);
					glTexCoord2f(xt[i+1]*w, yt[j+1]*h);  glVertex3d(0,y-ys[j]*height, x-xs[i]*width);	
					glTexCoord2f(xt[i+1]*w, yt[j]*h);    glVertex3d(0,y             , x-xs[i]*width);	
				glEnd();
			}

			y-=height*ys[j];
		}
		y=height;
		x-=width*xs[i];
	}
}

void drawNorthWall(int n){

	useWallMaterial();

	double width = n*3.0;
	double height= n*1.5;
	double w=3.0,h=1;
	double x=width,y=height;
	
	double ys[] = {0.45,0.36,0.19};
	double xs[] = {0.20,0.16,0.64};

	double yt[] = {0,0.45,0.81,1};
	double xt[] = {0,0.20,0.36,1};
			
	for(int i=0; i<3; ++i){
		for(int j=0; j<3; ++j){
			if(!((j == 2|| j==1) && i == 1)){
				glBegin(GL_POLYGON);
					glNormal3d(0.0, 0.0, 1.0);	
					glTexCoord2f(xt[i+1]*w, yt[j]*h);    glVertex3d(x-xs[i]*width , y             , 0);
					glTexCoord2f(xt[i+1]*w, yt[j+1]*h);  glVertex3d(x-xs[i]*width , y-ys[j]*height, 0);
					glTexCoord2f(xt[i]*w  , yt[j+1]*h);  glVertex3d(x             , y-ys[j]*height, 0);
					glTexCoord2f(xt[i]*w  , yt[j]*h);    glVertex3d(x             , y             , 0);
				glEnd();
			}
			y-=height*ys[j];
		}
		y=height;
		x-=width*xs[i];
	}
}

void drawSouthWall(int n){

	useWallMaterial();

	double width = n*3.0;
	double height= n*1.5;
	double w=3.0,h=1;
	double x=width,y=height;

	glBegin(GL_POLYGON);
		glNormal3d(0.0, 0.0, -1.0);	
		glTexCoord2f(w, 0);  glVertex3d(x, y, 4.7*n);
		glTexCoord2f(w, h);  glVertex3d(x, 0, 4.7*n);
		glTexCoord2f(0, h);  glVertex3d(0, 0, 4.7*n);
		glTexCoord2f(0, 0);  glVertex3d(0, y, 4.7*n);
	glEnd();
}

void drawPrinter(int n){

	useWoodMaterial();


	double length =0.92*n;
	double width  =0.64*n;
	double height =0.48*n;

	double w=1.6*1.9,h=1.2*2;
	double x=width,y=height;

	double ys[] = {0.33,0.66};
	double xs[] = {0.20,0.60,0.20};

	double yt[] = {1,0.67,0};
	double xt[] = {1,0.80,0.20,0};	

	glEnable(GL_TEXTURE_2D);
	glBindTexture(GL_TEXTURE_2D, printer_texture_number);

	for(int i=0; i<3; ++i){
		for(int j=0; j<2; ++j){
			if(!(j==1 && i == 1)){
				glBegin(GL_POLYGON);
				glNormal3d(0.0, 0.0, 1.0);	
				glTexCoord2f(xt[i+1]*w, yt[j]*h);    glVertex3d(x-xs[i]*width, y             , length);
				glTexCoord2f(xt[i+1]*w, yt[j+1]*h);  glVertex3d(x-xs[i]*width, y-ys[j]*height, length);
				glTexCoord2f(xt[i]*w  , yt[j+1]*h);  glVertex3d(x            , y-ys[j]*height, length);
				glTexCoord2f(xt[i]*w  , yt[j]*h);    glVertex3d(x            , y             , length);
				glEnd();
			}
			y-=height*ys[j];
		}
		y=height;
		x-=width*xs[i];
	}

	glBegin(GL_POLYGON);
	glNormal3d(1.0, 0.0, 0);	
	glTexCoord2f(0, h);  glVertex3d(width, height, length);
	glTexCoord2f(0, 0);  glVertex3d(width, 0, length);
	glTexCoord2f(w, 0);  glVertex3d(width, 0, 0);
	glTexCoord2f(w, h);  glVertex3d(width, height, 0);
	glEnd();

	glBegin(GL_POLYGON);
	glNormal3d(0.0, 0.0, -1.0);	
	glTexCoord2f(0, h);  glVertex3d(width, height, 0);
	glTexCoord2f(0, 0);  glVertex3d(width, 0, 0);
	glTexCoord2f(w, 0);  glVertex3d(0, 0, 0);
	glTexCoord2f(w, h);  glVertex3d(0, height, 0);
	glEnd();


	glBegin(GL_POLYGON);
	glNormal3d(-1.0, 0.0, 0.0);	
	glTexCoord2f(0, h);  glVertex3d(0, height, 0);
	glTexCoord2f(0, 0);  glVertex3d(0, 0, 0);
	glTexCoord2f(w, 0);  glVertex3d(0, 0, length);
	glTexCoord2f(w, h);  glVertex3d(0, height, length);
	glEnd();

	glBegin(GL_POLYGON);
	glNormal3d(0.0, 1.0, 0.0);	
	glTexCoord2f(0, h);  glVertex3d(0, height, length);
	glTexCoord2f(0, 0);  glVertex3d(width, height, length);
	glTexCoord2f(w, 0);  glVertex3d(width, height, 0);
	glTexCoord2f(w, h);  glVertex3d(0, height, 0);
	glEnd();

	glDisable(GL_TEXTURE_2D);
}

void drawRollingCarpet(int n,double b){

	useWallMaterial();


	glPushMatrix();
	glTranslated(-n*0.97,0,-n*0.77);

	//glTranslated(10*0.52,0,10*1.60);
	//glTranslated(-10*1.5,0,-10*4.7/2);
	//double length =4.6*n;
	//double width  =1.6*n* 0.6;
	//double height =1.2*n*0.33;

	double length =1.86*n;
	double width  =0.384*n;
	double height =0.1584*n;
	double w=8,h=3;
	if(w>=16) w=16;

	glEnable(GL_TEXTURE_2D);
	glBindTexture(GL_TEXTURE_2D, carpet_texture_number);

	//top
	glBegin(GL_POLYGON);
	glNormal3d(0.0, 1.0, 0.0);	
	glTexCoord2f(0+b, h);  glVertex3d(0    , height, length);
	glTexCoord2f(0+b, 0);  glVertex3d(width, height, length);
	glTexCoord2f(w+b, 0);  glVertex3d(width, height, 0);
	glTexCoord2f(w+b, h);  glVertex3d(0    , height, 0);
	glEnd();

	glDisable(GL_TEXTURE_2D);
	glPopMatrix();
}

void drawFloor(int n){

	double width = 4.7, height=3.4;

	useWoodMaterial();

	GLfloat ctrlpoints[4][3] = {{  0.0, 0.0, 0.0},
	{  n*height, 0, 0}, 
	{ 0, 0, n*width},
	{ n*height, 0, n*width} };

	GLfloat nrmlcompon[4][3] = {{  0.0, 1, 0.0},
	{  0.0, 1, 0.0}, 
	{  0.0, 1, 0.0},
	{  0.0, 1, 0.0} };

	GLfloat colorpoints[4][4] = {{ 1, 1, 1, 1},
	{ 1, 1, 1, 1}, 
	{ 1, 1, 1, 1},
	{ 1, 1, 1, 1}};

	double w=6.8,h=9.4;

	GLfloat textpoints[4][2] = {{ 0.0, h},
	{ w,   h}, 
	{ 0.0, 0.0},
	{ w,   0.0}};

	

	glEnable(GL_MAP2_VERTEX_3);
	glEnable(GL_MAP2_NORMAL);
	glEnable(GL_MAP2_TEXTURE_COORD_2);
	glEnable(GL_TEXTURE_2D);
	glBindTexture(GL_TEXTURE_2D, floor_texture_number);
	glMap2f(GL_MAP2_VERTEX_3, 0.0, 1.0, 3, 2,  0.0, 1.0, 6, 2,  &ctrlpoints[0][0]);
	glMap2f(GL_MAP2_NORMAL,   0.0, 1.0, 3, 2,  0.0, 1.0, 6, 2,  &nrmlcompon[0][0]);
	glMap2f(GL_MAP2_TEXTURE_COORD_2,  0.0, 1.0, 2, 2,  0.0, 1.0, 4, 2,  &textpoints[0][0]);
	glMapGrid2f(evaluatorXSegments, 0.0, 1.0, evaluatorYSegments, 0.0, 1.0); 

	
	glEvalMesh2(GL_FILL, 0, evaluatorXSegments, 0, evaluatorYSegments);
	glDisable(GL_MAP2_VERTEX_3);
	glDisable(GL_MAP2_NORMAL);
	glDisable(GL_MAP2_TEXTURE_COORD_2);
	glDisable(GL_TEXTURE_2D);
}

void drawCarpet(int n){

	useWoodMaterial();

	//double length =4.6*n;
	//double width  =1.6*n* 0.6;
	//double height =1.2*n*0.33;
	double length =1.84*n;
	double width  =0.64*n* 0.6;
	double height =0.48*n*0.33;

	double w=4.6*1.5,h=1;

	glEnable(GL_TEXTURE_2D);
	glBindTexture(GL_TEXTURE_2D, carpet_side_texture_number);

	//East
	glBegin(GL_POLYGON);
	glNormal3d(1.0, 0.0, 0);	
	glTexCoord2f(0, h);  glVertex3d(width, height, length);
	glTexCoord2f(0, 0);  glVertex3d(width, 0, length);
	glTexCoord2f(w, 0);  glVertex3d(width, 0, 0);
	glTexCoord2f(w, h);  glVertex3d(width, height, 0);
	glEnd();

	//south
	glBegin(GL_POLYGON);
	glNormal3d(0.0, 0.0, 1.0);	
	glTexCoord2f(width*1.5/n, h);  glVertex3d(0, height, length);
	glTexCoord2f(width*1.5/n, 0);  glVertex3d(0, 0, length);
	glTexCoord2f(0, 0);  glVertex3d(width, 0, length);
	glTexCoord2f(0, h);  glVertex3d(width, height, length);
	glEnd();

	//West
	glBegin(GL_POLYGON);
	glNormal3d(-1.0, 0.0, 0.0);	
	glTexCoord2f(0, h);  glVertex3d(0, height, 0);
	glTexCoord2f(0, 0);  glVertex3d(0, 0, 0);
	glTexCoord2f(w, 0);  glVertex3d(0, 0, length);
	glTexCoord2f(w, h);  glVertex3d(0, height, length);
	glEnd();
	glDisable(GL_TEXTURE_2D);
}

void moveCarpet(){
	if(CARPET_ENABLED){
		move+=carpetSpeed;
		if(move>=1) move =0;
	}
}

void spinCylinder(){
	if(PRESS_ENABLED){
		cylRot++;
		if(cylRot>=360) cylRot = 0;
	}
}

void drawPress(int n, double rot){

	gluQuadricDrawStyle(glQ, GLU_FILL);		
	gluQuadricNormals(glQ, GLU_SMOOTH);		
	gluQuadricOrientation(glQ, GLU_OUTSIDE);
	gluQuadricTexture(glQ, GL_TRUE);	

	//double width  =1.6*n* 0.6;
	//double height =1.2*n*0.33*0.52;
	usePaperMaterial();

	//values already simplified for minimum effort on a display routine
	static double width  =n* 0.384;
	static double height =n*0.082368;

	glEnable(GL_TEXTURE_2D);
	glBindTexture(GL_TEXTURE_2D, roll_texture_number);

	static double x = -n*0.97, y = height*2.9, z = -n*0.75;
	static int slices = 30, stacks = 10;

	glPushMatrix();
	glTranslated(x, y, z);
	glRotated(-rot,1,0,0);
	glPushMatrix();
	glRotated(90,0,1,0);
	gluCylinder(glQ, height, height, width, slices, stacks);
	glPopMatrix();

	glPushMatrix();
	glRotated(90,0,1,0);
	gluCylinder(glQ, 0, height, 0, slices, stacks);
	glPopMatrix();

	glPushMatrix();
	glTranslated(width,0,0);
	glRotated(-90,0,1,0);
	gluCylinder(glQ, 0, height, 0, slices, stacks);
	glPopMatrix();

	glPopMatrix();

	glDisable(GL_TEXTURE_2D);

}

void drawcube(double size){

	float n[6][3] =
	{
		{-1.0, 0.0, 0.0},
		{0.0, 1.0, 0.0},
		{1.0, 0.0, 0.0},
		{0.0, -1.0, 0.0},
		{0.0, 0.0, 1.0},
		{0.0, 0.0, -1.0}
	};
	int faces[6][4] =
	{
		{0, 1, 2, 3},
		{3, 2, 6, 7},
		{7, 6, 5, 4},
		{4, 5, 1, 0},
		{5, 6, 2, 1},
		{7, 4, 0, 3}
	};
	float v[8][3];
	GLint i;

	v[0][0] = v[1][0] = v[2][0] = v[3][0] = -size / 2;
	v[4][0] = v[5][0] = v[6][0] = v[7][0] = size / 2;
	v[0][1] = v[1][1] = v[4][1] = v[5][1] = -size / 2;
	v[2][1] = v[3][1] = v[6][1] = v[7][1] = size / 2;
	v[0][2] = v[3][2] = v[4][2] = v[7][2] = -size / 2;
	v[1][2] = v[2][2] = v[5][2] = v[6][2] = size / 2;

	for (i = 5; i >= 0; i--) {
		glBegin(GL_POLYGON);
		glNormal3fv(&n[i][0]);
		glTexCoord2f(1, 1); glVertex3fv(&v[faces[i][0]][0]);
		glTexCoord2f(0, 1); glVertex3fv(&v[faces[i][1]][0]);
		glTexCoord2f(0, 0); glVertex3fv(&v[faces[i][2]][0]);
		glTexCoord2f(1, 0); glVertex3fv(&v[faces[i][3]][0]);
		glEnd();
	}
}

void drawCleaverSupports(int n){

	double height =0.48*n*0.5;
	glDisable(GL_COLOR_MATERIAL);
	glMaterialfv(GL_FRONT_AND_BACK, GL_SHININESS, metal_shininess);
	glMaterialfv(GL_FRONT_AND_BACK, GL_SPECULAR,  metal_specular);
	glMaterialfv(GL_FRONT_AND_BACK, GL_DIFFUSE,   metal_diffuse);
	glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT,	  metal_ambient);

	double XZPlaneScale = 0.4;
	double leftSupportX = -n*0.9495, rightSupportX = -n*0.605, supportY = n*0.275, supportZ = n*0.4;

	glEnable(GL_TEXTURE_2D);
	glBindTexture(GL_TEXTURE_2D, metal_texture_number);
	glEnable(GL_NORMALIZE);
	glPushMatrix();
	glTranslated(leftSupportX,supportY,supportZ);
	glScaled(XZPlaneScale,height,XZPlaneScale);
	drawcube(1);
	glPopMatrix();

	glPushMatrix();
	glTranslated(rightSupportX,supportY,supportZ);
	glScaled(XZPlaneScale,height,XZPlaneScale);
	drawcube(1);
	glPopMatrix();
	glDisable(GL_NORMALIZE);
	glDisable(GL_TEXTURE_2D);
}

void drawCleaver(int n, double pos){
	double height =0.48*n*0.2;
	double width = n*0.005;
	double length =n*0.3046;
	double delta = n*0.1*pos;
	double w=3,h=1;

	double x = -n*0.625, y = n*0.265-delta, z = n*0.4;

	//Metal texture used as requested - also textured
	useMetalMaterial();

	glEnable(GL_TEXTURE_2D);
	glBindTexture(GL_TEXTURE_2D, cutter_texture_number);

	glPushMatrix();
	glTranslated(x,y,z);

	glBegin(GL_POLYGON);
	glNormal3d(0.0, 0.0, 1.0);	
	glTexCoord2f(0, h);  glVertex3d(-length, height, width);
	glTexCoord2f(0, 0);  glVertex3d(-length, 0, width);
	glTexCoord2f(w, 0);  glVertex3d(0, 0, width);
	glTexCoord2f(w, h);  glVertex3d(0, height, width);
	glEnd();

	glBegin(GL_POLYGON);
	glNormal3d(1.0, 0.0, 0.0);	
	glTexCoord2f(0, h);  glVertex3d(0, height, width);
	glTexCoord2f(0, 0);  glVertex3d(0, 0, width);
	glTexCoord2f(w, 0);  glVertex3d(0, 0, 0);
	glTexCoord2f(w, h);  glVertex3d(0, height, 0);
	glEnd();

	glBegin(GL_POLYGON);
	glNormal3d(0.0, 0.0, -1.0);	
	glTexCoord2f(0, h);  glVertex3d(0, height, 0);
	glTexCoord2f(0, 0);  glVertex3d(0, 0, 0);
	glTexCoord2f(w, 0);  glVertex3d(-length, 0, 0);
	glTexCoord2f(w, h);  glVertex3d(-length, height, 0);
	glEnd();

	glBegin(GL_POLYGON);
	glNormal3d(-1.0, 0.0, 0.0);	
	glTexCoord2f(0, h);  glVertex3d(-length, height, 0);
	glTexCoord2f(0, 0);  glVertex3d(-length, 0, 0);
	glTexCoord2f(w, 0);  glVertex3d(-length, 0, width);
	glTexCoord2f(w, h);  glVertex3d(-length, height, width);
	glEnd();

	glBegin(GL_POLYGON);
	glNormal3d(0.0, 1.0, 0.0);	
	glTexCoord2f(0, h);  glVertex3d(-length, height, 0);
	glTexCoord2f(0, 0);  glVertex3d(-length, height, width);
	glTexCoord2f(w, 0);  glVertex3d(0, height, width);
	glTexCoord2f(w, h);  glVertex3d(0, height, 0);
	glEnd();

	glBegin(GL_POLYGON);
	glNormal3d(0.0, -1.0, 0.0);	
	glTexCoord2f(w, h);  glVertex3d(0, 0, 0);
	glTexCoord2f(w, 0);  glVertex3d(0, 0, width);
	glTexCoord2f(0, 0);  glVertex3d(-length, 0, width);
	glTexCoord2f(0, h);  glVertex3d(-length, 0, 0);
	glEnd();
	glPopMatrix();

	glDisable(GL_TEXTURE_2D);
}

void moveCleaver(){
	if(CLEAVER_ENABLED){
		clvPos += clvDelta;
		if (clvPos>=1 || clvPos<=0) 
			clvDelta*=-1;
	}
	
}

void animateRobot(){
	static double zmin=12, xmin=-9.5, zmax=-19, xmax=5, xmid=1, zmid=8;

	if(robotPos == 0){
		robotZ=zmin;
		robotX=xmin;
	}

	//var's math calc reduction would have minimum impact
	if(ROBOT_ENABLED){
	static double a1=0.2568;
	static double a2=0.3971;
	
	if(robotPos <= 0 && speed<0){
		robotZ=zmin;
		robotX=xmin;
		speed*=-1;
		ROBOT_ENABLED=0;
		PAPER_ENABLED=1;
		PRESS_ENABLED=1;
		CLEAVER_ENABLED=1;
		CARPET_ENABLED=1;
	}
	else if(robotPos <= a1 && robotPos>0)
	{
		robotX=((xmid-xmin)*((robotPos)*(1/a1)))+xmin;
		robotZ=zmin;
		robotAng=0;
	} 
	else if(robotPos <= a2 && robotPos > a1)
	{
		double per = ((robotPos-a1)*(1/(a2-a1)));
		robotX=1+4*per;		
		robotZ=8+4*cos((PI/2)*per);
		robotAng=90*per;		
	} 
	else if(robotPos < 1 && robotPos > a2)
	{
		robotX=xmax;
		robotZ= zmax+(zmid-zmax)*(1-(robotPos-a2)*(1/(1-a2)));
		robotAng=90;
	} 
	else if(robotPos >= 1  && speed>0)
	{
		robotZ=zmax;
		robotX=xmax;
		speed*=-1;
		robotLoad=0;
		DRAWROBOTPAPER_ENABLE=0;
	}
	
	robotPos+=speed;
	}
}

void drawRobotList()
{
	glPushMatrix();
	glTranslated(robotX,0,robotZ);
	
	glRotated(robotAng,0,1,0);
	glCallList(ROBOT_LIST);

	double x = scale*0.18, y = 0, z = -scale*1.85;
	
	if(DRAWROBOTPAPER_ENABLE){
		double width=0.14*scale;
		double length = maxPaperSize;

		usePaperMaterial();

		glEnable(GL_TEXTURE_2D);
		glBindTexture(GL_TEXTURE_2D, roll_texture_number);
		glPushMatrix();
		glTranslated(x,y,z);
		glBegin(GL_POLYGON);
		glNormal3d(0.0, 1.0, 0.0);	
		glTexCoord2f(1, 0.9);  glVertex3d(-width, paperFallBottom, maxPaperSize-scale*0.2);
		glTexCoord2f(0, 0.9);  glVertex3d(-width, paperFallBottom, length);
		glTexCoord2f(0, 0.1);  glVertex3d(width, paperFallBottom, length);
		glTexCoord2f(1, 0.1);  glVertex3d(width, paperFallBottom, maxPaperSize-scale*0.2);
		glEnd();
		glPopMatrix();
		glDisable(GL_TEXTURE_2D);
	}

	glLightf(GL_LIGHT1, GL_SPOT_CUTOFF, 30.0);
	glLightfv(GL_LIGHT1, GL_SPOT_DIRECTION, spot_direction);
	glLightfv(GL_LIGHT1, GL_POSITION, light1_position);
	
	glPopMatrix();
}

void drawlargePaper(){

	double width=0.14*scale;
	double length=largePaperSize;
	double height=0.48*scale*0.34;

	double w=length/2, h=0.9;
	double x = -scale*0.77, y = 0, z = -scale*0.90;

	usePaperMaterial();

	glEnable(GL_TEXTURE_2D);
	glBindTexture(GL_TEXTURE_2D, roll_texture_number);

	glPushMatrix();
		glTranslated(x,y,z);

		glBegin(GL_POLYGON);
		glNormal3d(0.0, 1.0, 0.0);	
		glTexCoord2f(w, h);    glVertex3d(-width, height, 0);
		glTexCoord2f(0, h);    glVertex3d(-width, height, length);
		glTexCoord2f(0, 0.1);  glVertex3d(width, height, length);
		glTexCoord2f(w, 0.1);  glVertex3d(width, height, 0);
		glEnd();
	glPopMatrix();
	glDisable(GL_TEXTURE_2D);
}


void launchPaperFall(int d) 
{
	paperFallHeight-=paperFallSpeed;
	paperFallX+=paperFallLead;

	if(paperFallHeight > paperFallBottom)
		glutTimerFunc(DELTA_T,launchPaperFall,0);
	else {
		FALLINGPAPER_ENABLE=0;
		DRAWROBOTPAPER_ENABLE=1;
		if(robotLoad >= maxRobotLoad){
			ROBOT_ENABLED=1;
		}
	}
}

void animateLargePaper() {
	if(PAPER_ENABLED){
		if(largePaperSize<maxPaperSize){
			largePaperSize+=paperSpeed;
			paperTexturePos+=paperSpeed;
		}
		else{
			largePaperSize-=scale*0.2;
			paperFallHeight= 0.48*scale*0.34;
			paperFallX=0;
			if(FALLINGPAPER_ENABLE==0){
				glutTimerFunc(DELTA_T,launchPaperFall,0);
				FALLINGPAPER_ENABLE=1;
			}
			if(++robotLoad >= maxRobotLoad){
				PAPER_ENABLED=0;
				PRESS_ENABLED=0;
				CLEAVER_ENABLED=0;
				CARPET_ENABLED=0;
			}
		}
	}
}

void drawCameraText() {
	std::string text = "Camera ";
	text.push_back(camera + '0');
	drawText(textRComponent, textGComponent, textBComponent, cameraTextX, cameraTextY, text);
}

void animate(int d){

	animateLargePaper();
	moveCarpet();
	moveCleaver();
	animateRobot();
	spinCylinder();

	glutTimerFunc(DELTA_T, animate, 0);
}

void drawFallingPaper() 
{
	double x = -scale*0.77, y = 0, z = -scale*0.90+paperFallX; 
	if(FALLINGPAPER_ENABLE){
		double width=0.14*scale;
		double length = maxPaperSize;

		glEnable(GL_TEXTURE_2D);
		glBindTexture(GL_TEXTURE_2D, roll_texture_number);
		glPushMatrix();
		glTranslated(x, y, z);
		glBegin(GL_POLYGON);
		glNormal3d(0.0, 1.0, 0.0);	
		glTexCoord2f(1, 0.9);  glVertex3d(-width, paperFallHeight, maxPaperSize-scale*0.2);
		glTexCoord2f(0, 0.9);  glVertex3d(-width, paperFallHeight, length);
		glTexCoord2f(0, 0.1);  glVertex3d(width, paperFallHeight, length);
		glTexCoord2f(1, 0.1);  glVertex3d(width, paperFallHeight, maxPaperSize-scale*0.2);
		glEnd();
		glPopMatrix();
		glDisable(GL_TEXTURE_2D);
	}
}

void display(void)
{
	glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
	
	glMatrixMode( GL_PROJECTION );
	glLoadIdentity();
	glFrustum( -xy_aspect*.04, xy_aspect*.04, -.04, .04, .1, 500.0 );
	glMatrixMode( GL_MODELVIEW );
	glLoadIdentity();

	if(camera == 1)
	{
		glTranslatef( obj_pos[0], obj_pos[1], -obj_pos[2]-40 );
		glRotated( 20.0, 1.0, 0.0, 0.0);
		glRotated(-10.0, 0.0, 1.0, 0.0);
		glMultMatrixf( view_rotate );
	}
	else if(camera == 2)
	{
		gluLookAt(0,70,0,0,0,0,1,0,0);
	}
	else if(camera == 3)
	{
		gluLookAt(cam3x,cam3y,cam3z,robotX,0.3,robotZ,0,1,0);
	}

	if(showReferencial){
		glColorMaterial(GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE);
		glEnable(GL_COLOR_MATERIAL);

		glColor3f(1.0,0.0,0.0);		// vermelho
		glPushMatrix();
		glRotated(90.0, 0.0,1.0,0.0 );
		gluCylinder(glQ, axis_radius_begin, axis_radius_end,
			axis_lenght, axis_nslices, axis_nstacks);   
		glPopMatrix();

		glColor3f(0.0,1.0,0.0);		// verde
		glPushMatrix();
		glRotated(-90.0, 1.0,0.0,0.0 );
		gluCylinder(glQ, axis_radius_begin, axis_radius_end,
			axis_lenght, axis_nslices, axis_nstacks);  
		glPopMatrix();

		glColor3f(0.0,0.0,1.0);		// azul
		glPushMatrix();
		gluCylinder(glQ, axis_radius_begin, axis_radius_end,
			axis_lenght, axis_nslices, axis_nstacks);  
		glPopMatrix();
	}

	//LUZ0 - movivel atravez do teclado 
	glLightfv(GL_LIGHT0, GL_POSITION, light0_position);
	
	glColor3f(1.0,1.0,0.0);		
	gluQuadricOrientation( glQ, GLU_INSIDE);
	glPushMatrix();
	glTranslated(light0x,light0y,light0z);
	gluSphere(glQ, symb_light0_radius, symb_light0_slices, symb_light0_stacks);
	glPopMatrix();
	gluQuadricOrientation( glQ, GLU_OUTSIDE);
	glColor3f(1.0,1.0,1.0);
	glDisable(GL_COLOR_MATERIAL);
	
	//SCENE
	glCallList(STATIC_SCENE_LIST);
	glCallList(PRINTER_LIST);
	drawRobotList();
	drawRollingCarpet(scale,move);
	drawPress(scale,cylRot);
	drawCleaver(scale,clvPos);
	drawlargePaper();
	drawFallingPaper();

	//TEXT
	drawCameraText();
	if(help_on)
		drawHelpText();

	glutSwapBuffers();
}



/* Mouse handling */
void processMouse(int button, int state, int x, int y)
{
	if (button == GLUT_LEFT_BUTTON && state == GLUT_DOWN)
	{	 
	}
	if (button == GLUT_RIGHT_BUTTON && state == GLUT_DOWN)
	{	
	}

	glutPostRedisplay();

}

void processMouseMoved(int x, int y)
{
	// pedido de refrescamento da janela
	glutPostRedisplay();				
}

void processPassiveMouseMoved(int x, int y)
{
	// pedido de refrescamento da janela
	glutPostRedisplay();				
}

void reshape(int w, int h)
{
	int tx, ty, tw, th;

	GLUI_Master.get_viewport_area( &tx, &ty, &tw, &th );
	glViewport( tx, ty, tw, th );
	xy_aspect = (float)tw / (float)th;

	glutPostRedisplay();
}


void keyboard(unsigned char key, int x, int y)
{
	switch (key) {
	case 27:		// tecla de escape termina o programa
		exit(0);
		break;
	case '1':
		camera = 1;
		break;
	case '2':
		camera = 2;
		break;
	case '3':
		camera = 3;
		break;
	case 't':

		for(int i=0; i<4;++i)
			light0_diffuse[i]+=2;
		glLightfv(GL_LIGHT0, GL_DIFFUSE, light0_diffuse);

		break;
	case 'y':

		for(int i=0; i<4;++i)
			light0_diffuse[i]-=2;
		glLightfv(GL_LIGHT0, GL_DIFFUSE, light0_diffuse);
		break;

	case 's':
		light0_position[0]--;
		light0x--;
		break;
		
	case 'd':
		light0_position[2]++;
		light0z++;
		break;

	case 'e':
		light0_position[2]--;
		light0z--;
		break;

	case 'f':
		light0_position[0]++;
		light0x++;
		break;

	case 'w':
		light0_position[1]--;
		light0y--;
		break;
		
	case 'r':
		light0_position[1]++;
		light0y++;
		break;
	case 'j':
		cam3x-=cam3s;
		break;
	case 'l':
		cam3x+=cam3s;
		break;
	case 'k':
		cam3y-=cam3s;
		break;
	case 'i':
		cam3y+=cam3s;
		break;
	case 'q':
		cam3z-=cam3s;
		break;
	case 'a':
		cam3z+=cam3s;
		break;
	case 'h':
	case 'H':
		help_on = !help_on;
		break;
	}

}


void myGlutIdle( void )
{
	/* According to the GLUT specification, the current window is 
	undefined during an idle callback.  So we need to explicitly change
	it if necessary */
	if ( glutGetWindow() != main_window ) 
		glutSetWindow(main_window);  

	glutPostRedisplay();

}

void buildStaticSceneList() 
{
	glNewList(STATIC_SCENE_LIST,GL_COMPILE);
	drawCleaverSupports(scale);
	glPushMatrix();
	glTranslated(-scale*1.5,0,-scale*4.7/2);
	drawFloor(scale);
	glPushMatrix();
	glTranslated(0,scale*0.5,scale*0.7);
	drawSkybox();
	glPopMatrix();
	glEnable(GL_TEXTURE_2D);
	glBindTexture(GL_TEXTURE_2D, 2);
	drawWestWall(scale);
	drawNorthWall(scale);
	drawSouthWall(scale);
	glPushMatrix();	
	glTranslated(scale*3,0,0);
	drawCurvedWall(scale, 30, 0.5);
	glPopMatrix();
	glDisable(GL_TEXTURE_2D);
	//CEILING/////////////
	glPushMatrix();
	glTranslated(0,15,0);
	glRotated(90,1,0,0);
	glRectd(0,0,scale*3.5,scale*4.7);
	glPopMatrix();	
	//////////////////////
	glPushMatrix();
	glTranslated(scale*0.73,scale*0.555,scale*1.1);
	glEnable(GL_COLOR_MATERIAL);
	glColor3d(0.7,0.7,0.7);
	glFrontFace(GL_CW);
	glutSolidTeapot(scale/10); 
	glFrontFace(GL_CCW); 
	glDisable(GL_COLOR_MATERIAL);
	glPopMatrix();
	glPopMatrix();
	glEndList();
}

void buildPrinterList() 
{
	glNewList(PRINTER_LIST, GL_COMPILE);
	glPushMatrix();
	glTranslated(-scale*1.5,0,-scale*4.7/2);
	glPushMatrix();
	glTranslated(scale*0.4,0,scale*0.7);
	drawPrinter(scale);
	glPopMatrix();

	glPushMatrix();
	glTranslated(scale*0.53,0,scale*1.60);
	drawCarpet(scale);
	glPopMatrix();
	glPopMatrix();
	glEndList();
}

void buildRobotList() 
{
	glNewList(ROBOT_LIST, GL_COMPILE);
	drawRobot(scale);
	glEndList();
}

void init()
{
	// inicializa??o de apontador para qu?dricas
	glQ = gluNewQuadric();

	glFrontFace(GL_CCW);		// Front faces defined using a counterclockwise rotation
	glDepthFunc(GL_LEQUAL);		// Por defeito e GL_LESS
	glEnable(GL_DEPTH_TEST);	// Use a depth (z) buffer to draw only visible objects


	// Face Culling para aumentar a velocidade
	glEnable(GL_CULL_FACE);
	glCullFace(GL_BACK);		

	// Define que modelo de iluminacao utilizar; consultar o manual de referencia
	glLightModelfv(GL_LIGHT_MODEL_AMBIENT, light_ambient);  // define luz ambiente
	glLightModelf (GL_LIGHT_MODEL_TWO_SIDE, GL_FALSE);
	glLightModeli (GL_LIGHT_MODEL_LOCAL_VIEWER, 1);

	glClearColor(0.408,0.647,0.825,1.0);    

	glLightfv(GL_LIGHT0, GL_AMBIENT, light0_ambient);
	glLightfv(GL_LIGHT0, GL_DIFFUSE, light0_diffuse);
	glLightfv(GL_LIGHT0, GL_SPECULAR, light0_specular);
	glLightf(GL_LIGHT0, GL_CONSTANT_ATTENUATION,  light0_kc);
	glLightf(GL_LIGHT0, GL_LINEAR_ATTENUATION,    light0_kl);
	glLightf(GL_LIGHT0, GL_QUADRATIC_ATTENUATION, light0_kq);

	glLightfv(GL_LIGHT1, GL_AMBIENT, light1_ambient);
	glLightfv(GL_LIGHT1, GL_DIFFUSE, light1_diffuse);
	glLightfv(GL_LIGHT1, GL_SPECULAR, light1_specular);
	glLightf(GL_LIGHT1, GL_CONSTANT_ATTENUATION,  light1_kc);
	glLightf(GL_LIGHT1, GL_LINEAR_ATTENUATION,    light1_kl);
	glLightf(GL_LIGHT1, GL_QUADRATIC_ATTENUATION, light1_kq);

	glEnable(GL_LIGHTING);
	glEnable(GL_LIGHT0);
	glEnable(GL_LIGHT1);
	
	//glTexEnvf (GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);

	glShadeModel(GL_SMOOTH);			// GL_FLAT / GL_SMOOTH
	glPolygonMode(GL_FRONT, GL_FILL);	// preence a face da frente dos poligonos

	//Textyre loading
	pixmap.readBMPFile(skybox_texture);
	pixmap.setTexture(skybox_texture_number);

	pixmap.readBMPFile(wall_texture);
	pixmap.setTexture(wall_texture_number);

	pixmap.readBMPFile(printer_texture);
	pixmap.setTexture(printer_texture_number);

	pixmap.readBMPFile(carpet_side_texture);
	pixmap.setTexture(carpet_side_texture_number);

	pixmap.readBMPFile(carpet_texture);
	pixmap.setTexture(carpet_texture_number,1);

	pixmap.readBMPFile(roll_texture);
	pixmap.setTexture(roll_texture_number,1);

	pixmap.readBMPFile(cutter_texture);
	pixmap.setTexture(cutter_texture_number);

	pixmap.readBMPFile(floor_texture);
	pixmap.setTexture(floor_texture_number);

	pixmap.readBMPFile(metal_texture);
	pixmap.setTexture(metal_texture_number,1);

	buildStaticSceneList();
	buildPrinterList();
	buildRobotList();
}

void terminate()
{
	gluDeleteQuadric(glQ);
}

GLUI_Button *light_b;

void button_controller(int a){
	robot_light = !robot_light;
	if(robot_light){
		light_b->set_name("Desigar Luz do Robot");
		glEnable(GL_LIGHT1);
	}
	else{
		light_b->set_name("Ligar Luz do Robot");
		glDisable(GL_LIGHT1);
	}
}

void wireframe_controller(int a){
	if(wireframe_e){
		glClearColor(0.0,0.0,0.0,1.0); 
		glPolygonMode( GL_FRONT_AND_BACK, GL_LINE );
	}
	else{
		glClearColor(0.408,0.647,0.825,1.0); 
		glPolygonMode( GL_FRONT_AND_BACK, GL_FILL );
	}
}

void ref_controller(int a){
}


int main(int argc, char* argv[])
{
	glutInit(&argc, argv);
	glutInitDisplayMode(GLUT_DEPTH | GLUT_DOUBLE | GLUT_RGBA);
	glutInitWindowSize (DIMX, DIMY);
	glutInitWindowPosition (INITIALPOS_X, INITIALPOS_Y);
	main_window = glutCreateWindow (argv[0]);

	glutDisplayFunc(display);
	GLUI_Master.set_glutReshapeFunc(reshape);
	GLUI_Master.set_glutKeyboardFunc (keyboard);
	GLUI_Master.set_glutMouseFunc(processMouse);
	glutMotionFunc(processMouseMoved);
	glutPassiveMotionFunc(processPassiveMouseMoved);
	GLUI_Master.set_glutSpecialFunc( NULL );

	glutTimerFunc(DELTA_T, animate, 0);

	/*** Create the bottom subwindow ***/
	glui2 = GLUI_Master.create_glui_subwindow( main_window, GLUI_SUBWINDOW_BOTTOM );
	glui2->set_main_gfx_window( main_window );

	GLUI_Rotation *view_rot = glui2->add_rotation( "Rotacao", view_rotate );
	view_rot->set_spin( 1.0 );

	glui2->add_column( false );
	GLUI_Translation *trans_z = glui2->add_translation( "Zoom", GLUI_TRANSLATION_Z, &obj_pos[2] );
	trans_z->set_speed( .10 );

	glui2->add_column(false);
	glui2->add_statictext("Prima H para ajuda");
	glui2->add_checkbox("Use wireframe",&wireframe_e,2,wireframe_controller);
	glui2->add_checkbox("Mostrar referencial",&showReferencial,3,ref_controller);
	light_b = glui2->add_button("Desligar Luz do Robot",1,button_controller);
	
	/* We register the idle callback with GLUI, not with GLUT */
	GLUI_Master.set_glutIdleFunc( myGlutIdle );

	init();

	glutMainLoop();

	terminate();

	return 0;
}